home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / LIB / TEXT_EDI.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  24.9 KB  |  750 lines

  1.  
  2. package sub_arctic.lib;
  3.  
  4.  
  5. import sub_arctic.input.text_acceptor;
  6. import sub_arctic.input.pressable;
  7. import sub_arctic.input.simple_draggable;
  8. import sub_arctic.input.event;
  9.  
  10. import java.awt.Font;
  11. import java.awt.Point;
  12.  
  13. /** 
  14.  * Multi-line (but single font) text edit area.  This class allows simple
  15.  * entry and editing of multiple lines of text.
  16.  * 
  17.  * @author Scott Hudson
  18.  */
  19. public class text_edit extends text_display 
  20.   implements text_acceptor, pressable, simple_draggable {
  21.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  22.  
  23.   /** 
  24.    * Full constructor with explicit size.  Text contents is provided by 
  25.    *  one string with lines separated by newlines or carriage returns. 
  26.    * 
  27.    * @param int     xv       the x position of the object.
  28.    * @param int     yv       the y position of the object.
  29.    * @param int     wv       the width of the object.
  30.    * @param int     hv       the height of the object.
  31.    * @param String  contents the string to put into the editing area.
  32.    * @param boolean boxed    true if this object should have a box around it.
  33.    */
  34.   public text_edit(
  35.     int xv, int yv, int wv, int hv,
  36.     String           contents,
  37.     Font             fnt,
  38.     boolean          boxed) 
  39. {
  40.       super(xv,yv,wv,hv, contents, fnt, boxed);
  41.     }
  42.  
  43.    //had:
  44.    //* @exception general PROPAGATED
  45.  
  46.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  47.  
  48.   /** 
  49.    * Full constructor with size determined by initial text.  Text contents 
  50.    * is provided by one string with lines separated by newlines or carriage
  51.    * returns. 
  52.    *
  53.    * @param int     xv       the x position of the object.
  54.    * @param int     yv       the y position of the object.
  55.    * @param String  contents the string to put into the editing area.
  56.    * @param boolean boxed    true if this object should have a box around it.
  57.    */
  58.   public text_edit(
  59.     int xv, int yv, 
  60.     String           contents,
  61.     Font             fnt,
  62.     boolean          boxed) 
  63. {
  64.       super(xv,yv, contents, fnt, boxed);
  65.     }
  66.  
  67.    //had:
  68.    //* @exception general PROPAGATED
  69.  
  70.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  71.  
  72.   /** 
  73.    * Constructor with computed size, default font, and default 
  74.    * boxed status. 
  75.    *
  76.    * @param int    xv       the x position of the object.
  77.    * @param int    yv       the y position of the object.
  78.    * @param String contents the string to put into the editing area.
  79.    */
  80.   public text_edit(int xv, int yv, String contents) 
  81. {
  82.       super(xv,yv,contents);
  83.     }
  84.  
  85.    //had:
  86.    //* @exception general PROPAGATED
  87.  
  88.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  89.  
  90.   /** 
  91.    * Replace the area of the current selection with the given string.  The
  92.    * selection is set to a single point after the new text.  Since the 
  93.    * the replacement string can be empty, this can be used to delete the 
  94.    * selection.  
  95.    * 
  96.    * @param String with_str the new string to put where the selection is now.
  97.    */
  98.   public void replace_selection(String with_str)
  99.     {
  100.       String line, before, after, rest;
  101.       int nl, nl1, nl2, insert_loc, insert_pos, str_pos;
  102.  
  103.       /* if they pass a null treat that as an empty string */
  104.       if (with_str == null) with_str = "";
  105.  
  106.       /* if there is no selection, bail out early */
  107.       if (selection_start_line() == NO_SELECTION) return;
  108.  
  109.       /* extract the pieces before and after the selection */
  110.       line = (String)text().elementAt(selection_start_line());
  111.       before = line.substring(0,selection_start_pos());
  112.       line =  (String)text().elementAt(selection_end_line());
  113.       after = line.substring(selection_end_pos());
  114.  
  115.       /* remove all the lines that include selection parts */
  116.       for (int cnt=0; cnt<selection_end_line()-selection_start_line()+1; cnt++)
  117.     {
  118.       text().removeElementAt(selection_start_line());
  119.     }
  120.  
  121.       /* add in new string breaking up into lines as we go */
  122.       line = before;
  123.       insert_loc = selection_start_line();
  124.       insert_pos = selection_start_pos();
  125.       for (str_pos = 0; ; str_pos = nl+1)
  126.     {
  127.       /* find the first line break after the current pos in new string */
  128.       nl1 = with_str.indexOf('\n', str_pos);
  129.       nl2 = with_str.indexOf('\r', str_pos);
  130.       if (nl1 < nl2 || nl2 == -1)
  131.         nl = nl1;
  132.       else
  133.         nl = nl2;
  134.  
  135.       /* if there is no newline append the rest of the string plus the part
  136.        * after the selection and we are done */
  137.       if (nl == -1)
  138.         {
  139.           rest = with_str.substring(str_pos);
  140.           text().insertElementAt(line + rest + after, insert_loc);
  141.           insert_pos += rest.length();
  142.           break;
  143.         }
  144.       else
  145.         {
  146.           /* otherwise append the piece we have and move to a new line */
  147.           text().insertElementAt(line + with_str.substring(str_pos,nl), 
  148.                                    insert_loc);
  149.           insert_loc++;
  150.           insert_pos = 0;
  151.           line = "";
  152.         }
  153.     }
  154.  
  155.       /* set selection to be after the last inserted text */
  156.       set_selection(insert_loc, insert_pos, insert_loc, insert_pos);
  157.  
  158.       damage_self();
  159.     } 
  160.  
  161.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  162.  
  163.   /** 
  164.    * Delete the character before the current selection (backing the selection
  165.    * up one character).  If there is no selection or the selection starts at
  166.    * the beginning of the string, nothing happens.  
  167.    */
  168.   public void delete_a_char()
  169.     {
  170.       int prev_line, prev_len;
  171.       String line, before, after;
  172.  
  173.       /* if there is no selection or we are at the start of the text, bail now*/
  174.       if (selection_start_line() == NO_SELECTION || 
  175.      (selection_start_line() == 0 && selection_start_pos() == 0))
  176.     return;
  177.  
  178.       /* if selection is at the start of a line do a join with previous line */
  179.       if (selection_start_pos() == 0)
  180.     {
  181.       /* concat selection line onto prev line */
  182.       prev_line = selection_start_line()-1;
  183.       line = (String)text().elementAt(prev_line);
  184.       prev_len = line.length();
  185.       line = line + (String)text().elementAt(selection_start_line());
  186.  
  187.       /* use new line, get rid of old line */
  188.       text().setElementAt(line, prev_line);
  189.       text().removeElementAt(selection_start_line());
  190.  
  191.       /* Set selection point at end of old text */
  192.       set_selection(prev_line, prev_len, prev_line, prev_len);
  193.  
  194.       damage_self();
  195.     }
  196.       /* otherwise we just kill off a character */
  197.       else
  198.     {
  199.       /* extract piece of line before selection, less on character */
  200.       line = (String)text().elementAt(selection_start_line());
  201.       before = line.substring(0, selection_start_pos()-1);
  202.  
  203.       /* and piece after selection point */
  204.       after = line.substring(selection_start_pos());
  205.  
  206.       /* replace text with concat of that */
  207.       text().setElementAt(before+after, selection_start_line());
  208.  
  209.       /* move selection point back one */
  210.       set_selection(selection_start_line(), selection_start_pos()-1, 
  211.                     selection_start_line(), selection_start_pos()-1);
  212.  
  213.       damage_self();
  214.     }
  215.     }
  216.  
  217.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  218.  
  219.   /** 
  220.    * Map an x position in local coordinates into a text string position (index)
  221.    * on a given line. 
  222.    *
  223.    * @param int on_ln the line to be checking.
  224.    * @param int x_pos the x coordinate to check (in pixels).
  225.    * @return int the character number
  226.    */
  227.   public int char_pos_of(int on_ln, int x_pos)
  228.     {
  229.       int len, front, mid, back, dist;
  230.       String line;
  231.       char[] ln_text;
  232.  
  233.       /* first take off x margin */
  234.       x_pos -= h_spacing();
  235.  
  236.       /* take care of positions before start */
  237.       if (x_pos < 0) return 0;
  238.  
  239.       /* extract character array from string */
  240.       line = (String)text().elementAt(on_ln);
  241.       len = line.length();
  242.       ln_text = new char[len];
  243.       line.getChars(0,len,ln_text,0);
  244.  
  245.       /* binary search to find the text position before the given point */
  246.       front = 0; 
  247.       back = len;
  248.       do {
  249.     mid = (front + back)/2;
  250.     dist = x_pos - _metric.charsWidth(ln_text, 0, mid);
  251.     if (dist > 0)
  252.       front = mid;
  253.     else if (dist == 0)
  254.       return mid;
  255.     else
  256.       back = mid; 
  257.       } while (front+1 < back);
  258.  
  259.       /* if we are at the end just take that */
  260.       if (mid == len) return front;
  261.  
  262.       /* now see if we are more than 1/2 way into the final character */
  263.       if (Math.abs(x_pos - _metric.charsWidth(ln_text, 0, front)) > 
  264.           Math.abs(x_pos-_metric.charsWidth(ln_text, 0, front+1)))
  265.     return front + 1;
  266.       else
  267.     return front;
  268.     }
  269.  
  270.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  271.  
  272.   /** 
  273.    * Map y position in local coordinates into a line number.  If the position 
  274.    * is before the first line (index 0) then -1 is returned.  If the position 
  275.    * is past the last line, then one greater than the index of the last line 
  276.    * is returned. 
  277.    * 
  278.    * @param int y_pos the y position (in pixels) to map to a line number.
  279.    * @return int a line number, -1, or 1+ the last line number.
  280.    */
  281.   public int find_line_of(int y_pos)
  282.     {
  283.       int result;
  284.  
  285.       /* compensate for v_spacing at top */
  286.       y_pos -= v_spacing();
  287.  
  288.       /* find our line */
  289.       result = y_pos / _metric.getHeight();
  290.  
  291.       /* account for first line offset */
  292.       result += first_line();
  293.  
  294.       /* take care of before beginning case */
  295.       if (result < 0) result = -1;
  296.  
  297.       /* take care of after end case */
  298.       if (result > text().size()) result = text().size();
  299.  
  300.       return result;
  301.     }
  302.  
  303.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  304.   /* text_acceptor (input) methods */
  305.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  306.  
  307.   //xx later we need to track how is the focus and draw focus/non differently
  308.  
  309.   /** 
  310.    * Accept input that signifies that object is new text focus and will
  311.    * receive subsequent text input.  Returns true if the object accepts the
  312.    * focus.  
  313.    * 
  314.    * @param event  evt       the event to be dispatched.
  315.    * @param Object user_info the object provided to the agent when this 
  316.    *                         interactor entered the focus set.
  317.    * @return boolean true if this event was handled (which it always is in 
  318.    *                      this case).
  319.    */
  320.   public boolean start_text_entry(event evt, Object user_info)
  321.     {
  322.       return true;
  323.     }
  324.  
  325.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  326.  
  327.   /** 
  328.    * Pre-filter a character before providing it is actually provided as input.
  329.    * This routine gets called to allow the object to modify the character 
  330.    * before it is passed to new_char().  Input to the method is the ordinal 
  331.    * value of the input character in question along with the modifier mask 
  332.    * associated with it.  Output should either be the ordinal value of a  
  333.    * character or a specific negative value signifying one of several special 
  334.    * actions.  This routine can be used by subclasses to do a translation 
  335.    * (e.g. to all lower case) or to filter out unwanted characters (e.g. 
  336.    * everything except decimal digits). Filtering is done by returning the 
  337.    * special value DISCARD_CHAR (= -1), which signifies that the character 
  338.    * is not to be passed to new_char().  Translation is done by returning 
  339.    * the ordinal value of the translated character.  In addition, the value 
  340.    * CLOSURE_ACTION_CHAR can be returned to indicate that the action_char() 
  341.    * method should be invoked instead of new_char().  This is typically done 
  342.    * for end of line characters that signify completion of an entry.  All 
  343.    * modifications to the character are considered local to this object and 
  344.    * do not change how the character might be delivered to another object.  
  345.    * This routine is not called for cursor movement or other special keys 
  346.    * (which are dispatched with special_key()), or characters classified  
  347.    * as edit keys (e.g. to delete a character or line).  The text input 
  348.    * dispatch agent class (text_agent) provides several standard filters that 
  349.    * can be  called for common operations.
  350.    * 
  351.    * @param int input_char the input character to be filtered.
  352.    * @param int modifiers  the current state of the modifier keys.
  353.    * @return int the filtered value to enter into the object or a special 
  354.    *             value (DISCARD_CHAR or CLOSURE_ACTION_CHAR)
  355.    */
  356.   public int char_filter(int input_char, int modifiers)
  357.     {
  358.       /* here we do no filtering and no translation */
  359.       return input_char;
  360.     }
  361.  
  362.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  363.  
  364.   /** 
  365.    * Accept input for a single character.  Returns true if the object
  366.    * consumes the character.
  367.    *
  368.    * @param event   evt      the event to be dispatched.
  369.    * @param char   ch        the character to be entered.
  370.    * @param Object user_info the object provided to the agent when this 
  371.    *                         interactor entered the focus set.
  372.    * @return boolean true if this event was handled (which it always is in 
  373.    *                      this case).
  374.    */
  375.   public boolean new_char(event evt, char ch, Object user_info)
  376.     {
  377.       /* if we don't have any text at all, let's just set the
  378.      text to be this value */
  379.       if ((text()==null) || (text().size()==0)) {
  380.     set_text(String.valueOf(ch));
  381.     return true;
  382.       }
  383.       /* if we have no selection, make one at the end */
  384.       if (selection_start_line() == NO_SELECTION)
  385.     set_selection(SELECT_END,SELECT_END, SELECT_END,SELECT_END);
  386.  
  387.       /* replace selection with the char */
  388.       replace_selection(String.valueOf(ch));
  389.  
  390.       return true;
  391.     }
  392.  
  393.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  394.  
  395.   /** 
  396.    * Accept input for a character that has been classified as a closure
  397.    * action (by returning CLOSURE_ACTION_CHAR from char_filter).  
  398.    * 
  399.    * @param event  evt       the event to be dispatched.
  400.    * @param Object user_info the object provided to the agent when this 
  401.    *                         interactor entered the focus set.
  402.    * @return boolean true if this event was handled (which it always is in 
  403.    *                         this case).
  404.    */
  405.   public boolean action_char(event evt, char ch, Object user_info)
  406.     {
  407.       /* Here we consume, but ignore closure characters */
  408.       return true;
  409.     }
  410.  
  411.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  412.  
  413.   /** 
  414.    * Accept input for a character signifying a delete.  Returns true if the
  415.    * object consumes the input.  
  416.    *
  417.    * @param event  evt       the event to be dispatched.
  418.    * @param Object user_info the object provided to the agent when this 
  419.    *                         interactor entered the focus set.
  420.    * @return boolean true if this event was handled (which it always is in 
  421.    *                      this case).
  422.    */
  423.   public boolean delete_char(event evt, Object user_info)
  424.     {
  425.       /* if we have no selection, we do nothing */
  426.       if (selection_start_line() == NO_SELECTION) 
  427.     return true;
  428.  
  429.       /* if we have a point selection delete back from it */
  430.       if (selection_start_line() == selection_end_line() && 
  431.       selection_start_pos()  == selection_end_pos())
  432.         delete_a_char();
  433.       /* otherwise replace selection with an empty string */
  434.       else
  435.     replace_selection("");
  436.  
  437.       return true;
  438.     }
  439.  
  440.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  441.  
  442.   /** 
  443.    * Accept input for a character signifying a line kill.  Here we treat this
  444.    * the same as a delete of the current selection. 
  445.    *
  446.    * @param event  evt       the event to be dispatched.
  447.    * @param Object user_info the object provided to the agent when this 
  448.    *                         interactor entered the focus set.
  449.    * @return boolean true if this event was handled (which it always is in 
  450.    *                      this case).
  451.    */
  452.   public boolean line_kill(event evt, Object user_info)
  453.     {
  454.       /* delete the current selection */
  455.       replace_selection("");
  456.  
  457.       return true;
  458.       // may need to reconsider what this should mean in multi-line editor
  459.     }
  460.  
  461.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  462.  
  463.   /** 
  464.    * Accept input for a key press signifying a special key (corresponding
  465.    * to KEY_ACTION events).  Values passed here are from the key field of the
  466.    * events.  These include: DOWN, END, F1, ..., F12, HOME, LEFT, 
  467.    * PGDN, PGUP, RIGHT, and UP.  Returns true if the object consumes 
  468.    * the event.
  469.    *
  470.    * @param event  evt       the event to be dispatched.
  471.    * @param int    key_code  the keyboard code of this special key.
  472.    * @param Object user_info the object provided to the agent when this 
  473.    *                         interactor entered the focus set.
  474.    * @return boolean true if this event was handled (which it always is in 
  475.    *                      this case).
  476.    */
  477.   public boolean special_key(event evt, int key_code, Object user_info)
  478.     {
  479.       int ln, pos, len;
  480.  
  481.       /* handle LEFT and RIGHT only */
  482.       if (key_code == event.LEFT)
  483.     {
  484.       /* if we have no selection, we are done */
  485.       if (selection_start_line() == NO_SELECTION) return false;
  486.  
  487.       /* if we have a point selection, just move */
  488.           if (selection_start_line() == selection_end_line() && 
  489.           selection_start_pos()  == selection_end_pos())
  490.         {
  491.           /* only move if we are not at the beginning of text */
  492.           if (selection_start_line() != 0 || selection_start_pos() != 0)
  493.         {
  494.           /* if we are the beginning of a line */
  495.           if (selection_start_pos() == 0)
  496.             {
  497.               /* move to back of previous line */
  498.               ln = selection_start_line()-1;
  499.               pos = ((String)text().elementAt(ln)).length();
  500.             }
  501.           else
  502.             {
  503.               /* otherwise just back up with the same line */
  504.               ln = selection_start_line();
  505.               pos = selection_start_pos()-1;
  506.             }
  507.               set_selection(ln,pos,ln,pos);
  508.         }
  509.         }
  510.       /* otherwise go to a point selection at the left of current */
  511.       else
  512.         {
  513.           set_selection(selection_start_line(), selection_start_pos(),
  514.                         selection_start_line(), selection_start_pos());
  515.         }
  516.  
  517.           return true;
  518.     }
  519.  
  520.       if (key_code == event.RIGHT)
  521.     {
  522.       /* if we have no selection, we are done */
  523.       if (selection_start_line() == NO_SELECTION) return false;
  524.  
  525.       /* if we have a point selection, just move */
  526.           if (selection_start_line() == selection_end_line() && 
  527.           selection_start_pos()  == selection_end_pos())
  528.         {
  529.           /* if we are at the end of a line */
  530.           len = ((String)text().elementAt(selection_start_line())).length();
  531.           if (selection_start_pos() > len-1)
  532.         {
  533.           /* move down a line unless we are at the end */
  534.                   if (selection_start_line() < text().size()-1)
  535.             {
  536.               ln = selection_start_line()+1;
  537.               pos = 0;
  538.                   set_selection(ln,pos,ln,pos);
  539.             }
  540.         }
  541.           else
  542.         {
  543.           /* otherwise just move forward with the same line */
  544.           ln = selection_start_line();
  545.           pos = selection_start_pos()+1;
  546.               set_selection(ln,pos,ln,pos);
  547.         }
  548.         }
  549.       /* otherwise go to a point selection at the right */
  550.       else
  551.         {
  552.           set_selection(selection_end_line(), selection_end_pos(),
  553.                         selection_end_line(), selection_end_pos());
  554.         }
  555.  
  556.           return true;
  557.     }
  558.  
  559.       /* we don't want anything else */
  560.       return false;
  561.     }
  562.  
  563.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  564.  
  565.   //xx later need to track focus and display differently
  566.  
  567.   /** 
  568.    * Dispatch input indicating that text input is over (e.g. the text focus
  569.    * has moved elsewhere.  Returns true if the object consumes the input.
  570.    *
  571.    * @param event  evt       the event to be dispatched.
  572.    * @param Object user_info the object provided to the agent when this 
  573.    *                         interactor entered the focus set.
  574.    * @return boolean true if this event was handled (which it always is in 
  575.    *                      this case).
  576.    */
  577.   public boolean end_text_entry(event evt, Object user_info)
  578.     {
  579.       /* here we do nothing */
  580.       return true;
  581.  
  582.       // later we need to go to another highlight style
  583.     }
  584.  
  585.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  586.   /* pressable (input) methods */
  587.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  588.  
  589.   /** 
  590.    * Accept mouse button press input to the object.  Return true if event 
  591.    * was consumed. 
  592.    *
  593.    * @param event  evt       the event to be dispatched.
  594.    * @param Object user_info the object provided to pick when this object was 
  595.    *                         clicked on.
  596.    * @return boolean true if this event was handled.
  597.    */
  598.   public boolean press(event evt, Object user_info)
  599.     {
  600.       /* make us both the text and drag focus */
  601.       manager.text_focus.set_focus_to(this, evt, user_info);
  602.       /* if we don't have any text, there isn't any sense in
  603.      doing a drag */
  604.       if ((text()==null) || (text().size()==0)) {
  605.     return true;
  606.       }
  607.       manager.simple_drag_focus.set_focus_to(this, evt, new Point(0,0));
  608.       return true;
  609.     }
  610.  
  611.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  612.  
  613.   /** 
  614.    * Companion method to press() needed to finish pressable.  Here we never
  615.    *  consume the event. 
  616.    *
  617.    * @param event  evt       the event to be dispatched.
  618.    * @param Object user_info the object provided to pick when this object was 
  619.    *                         clicked on.
  620.    * @return boolean true if this event was handled (it is never handled by 
  621.    *                      this object)
  622.    */
  623.   public boolean release(event evt, Object user_info)
  624.     {
  625.       return false;
  626.     }
  627.  
  628.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  629.   /* simple_draggable (input) methods */
  630.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  631.  
  632.   /** 
  633.    * Accept the start of a drag to the object.  
  634.    * 
  635.    * @param event  evt       the event to be dispatched.
  636.    * @param Object user_info the object provided to the agent when this 
  637.    *                         interactor entered the focus set.
  638.    * @return boolean true if this event was handled (which it always is in 
  639.    *                      this case).
  640.    */
  641.   public boolean drag_start(event evt, Object user_info)
  642.     {
  643.       int start_line, start_pos;
  644.  
  645.       /* establish the start of selection where this event was */
  646.       start_line = find_line_of(evt.local_y());
  647.       if (start_line < 0)
  648.     {
  649.       start_line = 0;
  650.       start_pos = 0;
  651.     }
  652.       else if (start_line >= text().size())
  653.     {
  654.       start_line = text().size()-1;
  655.       start_pos  = ((String)text().elementAt(start_line)).length();
  656.     }
  657.       else
  658.     {
  659.       start_pos = char_pos_of(start_line, evt.local_x());
  660.     }
  661.       set_selection(start_line, start_pos, start_line, start_pos);
  662.  
  663.       /* stash the star position in user_info (this is a bit of an abuse of a
  664.        * point object, but...) */
  665.       ((Point)user_info).x = start_line;
  666.       ((Point)user_info).y = start_pos;
  667.  
  668.       return true;
  669.  
  670.       //later need to handle "extend" case (w/ shift down)
  671.     }
  672.  
  673.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  674.  
  675.   /** 
  676.    * Accept a movement during a drag.  
  677.    *
  678.    * @param event  evt       the event to be dispatched.
  679.    * @param Object user_info the object provided to the agent when this 
  680.    *                         interactor entered the focus set.
  681.    * @return boolean true if this event was handled (which it always is in 
  682.    *                      this case).
  683.    */
  684.   public boolean drag_feedback(event evt, Object user_info)
  685.     {
  686.       int line, pos;
  687.       int start_line, start_pos;
  688.  
  689.       /* extract start loc from user_info */
  690.       start_line = ((Point)user_info).x;
  691.       start_pos  = ((Point)user_info).y;
  692.  
  693.       /* set end of selection here (we rely on set_selection() to flip) */
  694.       line = find_line_of(evt.local_y());
  695.       if (line < 0)
  696.     {
  697.       line = 0;
  698.       pos = 0;
  699.     }
  700.       else if (line >= text().size())
  701.     {
  702.       line = text().size()-1;
  703.       pos  = ((String)text().elementAt(line)).length();
  704.     }
  705.       else
  706.     {
  707.       pos = char_pos_of(line, evt.local_x());
  708.     }
  709.       set_selection(start_line, start_pos, line, pos);
  710.  
  711.  
  712.       return true;
  713.     }
  714.  
  715.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  716.  
  717.   /** 
  718.    * Accept input corresponding to the end of a drag.  
  719.    *
  720.    * @param event  evt       the event to be dispatched.
  721.    * @param Object user_info the object provided to the agent when this 
  722.    *                         interactor entered the focus set.
  723.    * @return boolean true if this event was handled (which it always is in 
  724.    *                      this case).
  725.    */
  726.   public boolean drag_end(event evt, Object user_info)
  727.     {
  728.       /* let drag_feedback do the work */
  729.       return drag_feedback(evt, user_info);
  730.     }
  731.  
  732.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  733. }
  734. /*=========================== COPYRIGHT NOTICE ===========================
  735.  
  736. This file is part of the subArctic user interface toolkit.
  737.  
  738. Copyright (c) 1996 Scott Hudson and Ian Smith
  739. All rights reserved.
  740.  
  741. The subArctic system is freely available for most uses under the terms
  742. and conditions described in 
  743.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  744. and appearing in full in the lib/interactor.java source file.
  745.  
  746. The current release and additional information about this software can be 
  747. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  748.  
  749. ========================================================================*/
  750.